// Package log 用于滚动将日志写入文件
//
// 示例:
// import gitee.com/golangx/log
// logOpt := &log.Option{
// 		MaxSize:       128,
// 		MaxBackups:    7,
// 		MaxAge:        1,
// 		Debug:         debug,
// 		ServiceName:   serviceName,
// 		TimeKey:       "time",
// 		LevelKey:      "level",
// 		NameKey:       "logger",
// 		CallerKey:     "caller",
// 		MessageKey:    "msg",
// 		StacktraceKey: "stacktrace",
// 	}
// logOpt.Start("/log")
// log.Debug("Hello, Debug!!!")
package log

import (
	"os"
	"strings"
	"time"

	"go.uber.org/zap"
	"go.uber.org/zap/zapcore"
	"gopkg.in/natefinch/lumberjack.v2"
)

// LoggerObj  zap日志
type LoggerObj struct {
	*zap.Logger
}

// NewLogger 新建logger
//  * filePath 日志文件路径
//  * level 日志级别
//  * maxSize 每个日志文件保存的最大尺寸 单位：M
//  * maxBackups 日志文件最多保存多少个备份
//  * maxAge 文件最多保存多少天
//  * compress 是否压缩
//  * serviceName 服务名
func NewLogger(filePath string, level zapcore.Level) *zap.Logger {
	core := newCore(filePath, level, opt.MaxSize, opt.MaxBackups, opt.MaxAge, opt.Compress)
	if opt.Debug == true {
		return zap.New(core, zap.AddCaller(), zap.AddCallerSkip(1), zap.Development(), zap.Fields(zap.String("serviceName", opt.ServiceName)))
	}
	return zap.New(core, zap.AddCaller(), zap.AddCallerSkip(1), zap.Fields(zap.String("serviceName", opt.ServiceName)))
}

/**
 * zapcore构造
 */
func newCore(filePath string, level zapcore.Level, maxSize int, maxBackups int, maxAge int, compress bool) zapcore.Core {
	//日志文件路径配置2
	hook := lumberjack.Logger{
		Filename:   filePath,   // 日志文件路径
		MaxSize:    maxSize,    // 每个日志文件保存的最大尺寸 单位：M
		MaxBackups: maxBackups, // 日志文件最多保存多少个备份
		MaxAge:     maxAge,     // 文件最多保存多少天
		Compress:   compress,   // 是否压缩
	}
	// 设置日志级别
	atomicLevel := zap.NewAtomicLevel()
	atomicLevel.SetLevel(level)
	//公用编码器
	encoderConfig := zapcore.EncoderConfig{
		TimeKey:        "time",
		LevelKey:       "level",
		NameKey:        "logger",
		CallerKey:      "caller",
		MessageKey:     "msg",
		StacktraceKey:  "stacktrace",
		LineEnding:     zapcore.DefaultLineEnding,
		EncodeLevel:    zapcore.LowercaseLevelEncoder,  // 小写编码器
		EncodeTime:     shortTimeEncoder,               // ISO8601 UTC 时间格式
		EncodeDuration: zapcore.SecondsDurationEncoder, //
		EncodeCaller:   zapcore.ShortCallerEncoder,     // 简短路径编码器, 全路径编码器用zapcore.FullCallerEncoder,
		EncodeName:     zapcore.FullNameEncoder,
	}
	var ws zapcore.WriteSyncer
	if opt.Debug == true {
		encoderConfig.EncodeTime = shortTimeEncoder
		ws = zapcore.AddSync(os.Stdout) // 打印到控制台
	} else {
		encoderConfig.EncodeTime = longTimeEncoder
		ws = zapcore.AddSync(&hook) // 打印到文件
	}
	return zapcore.NewCore(
		zapcore.NewJSONEncoder(encoderConfig), // 编码器配置
		zapcore.NewMultiWriteSyncer(ws),       // 打印到控制台和文件
		atomicLevel,                           // 日志级别
	)
}

func shortTimeEncoder(t time.Time, enc zapcore.PrimitiveArrayEncoder) {
	enc.AppendString(t.Format(time.Kitchen))
}

func longTimeEncoder(t time.Time, enc zapcore.PrimitiveArrayEncoder) {
	enc.AppendString(t.Format("2006-01-02 15:04:05"))
}

var (
	// InfoLogger info级别日志
	InfoLogger LoggerObj
	// DebugLogger debug级别日志
	DebugLogger LoggerObj
	// WarnLogger warn级别日志
	WarnLogger LoggerObj
	// ErrorLogger error级别日志
	ErrorLogger LoggerObj
)

// Option 日志配置项
type Option struct {
	MaxSize       int    // 每个日志文件保存的最大尺寸 单位：M
	MaxBackups    int    // 日志文件最多保存多少个备份
	MaxAge        int    // 文件最多保存多少天
	Compress      bool   // 是否压缩
	ServiceName   string // 服务名
	Debug         bool
	TimeKey       string
	LevelKey      string // 日志级别的key
	CallerKey     string // 文件和行号的key
	MessageKey    string // Message的key
	NameKey       string
	StacktraceKey string
	BaseParams    interface{} // 请求基本参数
}

// opt 日志配置
var opt Option

// Start 使用配置项初始化日志
func (o *Option) Start(dir string) {
	opt = *o
	if strings.HasSuffix(dir, "/") == false {
		dir += "/"
	}
	DebugLogger.Logger = NewLogger(dir+o.ServiceName+".debug.log", zapcore.DebugLevel)
	InfoLogger.Logger = NewLogger(dir+o.ServiceName+".info.log", zapcore.InfoLevel)
	WarnLogger.Logger = NewLogger(dir+o.ServiceName+".warn.log", zapcore.WarnLevel)
	ErrorLogger.Logger = NewLogger(dir+o.ServiceName+".error.log", zapcore.ErrorLevel)
}

// Debug 记录一个debug日志
func Debug(msg string, fields ...interface{}) {
	if len(fields) > 0 {
		result := fields[0]
		fields := []zap.Field{
			zap.Any("content", result),
		}
		DebugLogger.Debug(msg, fields...)
		return
	}
	DebugLogger.Debug(msg)
}

// Info 记录一个info日志
func Info(msg string, fields ...interface{}) {
	if len(fields) > 0 {
		result := fields[0]
		fields := []zap.Field{
			zap.Any("content", result),
		}
		InfoLogger.Info(msg, fields...)
		return
	}
	InfoLogger.Info(msg)
}

// Warn 记录一个warn日志
func Warn(msg string, fields ...interface{}) {
	if len(fields) > 0 {
		result := fields[0]
		fields := []zap.Field{
			zap.Any("content", result),
		}
		WarnLogger.Warn(msg, fields...)
		return
	}
	WarnLogger.Warn(msg)
}

// Error 记录一个debug日志
func Error(msg string, fields ...interface{}) {
	if len(fields) > 0 {
		result := fields[0]
		fields := []zap.Field{
			zap.Any("content", result),
		}
		ErrorLogger.Error(msg, fields...)
		return
	}
	ErrorLogger.Error(msg)
}

// LoggerImp LoggerImp
type LoggerImp struct {
	Base map[string]string
}

// NewWebLogger NewLogger
func NewWebLogger() *LoggerImp {
	return new(LoggerImp)
}

// SetBase 获取基础参数
func (l *LoggerImp) SetBase(base map[string]string) {
	l.Base = base
}

// Debug debug日志实现
func (l *LoggerImp) Debug(msg string, fields ...interface{}) {
	if len(fields) > 0 {
		result := fields[0]
		if opt.Debug == true {
			DebugLogger.Debug(msg, zap.Any("content", result))
		} else {
			DebugLogger.Debug(msg, zap.Any("base", l.Base), zap.Any("content", result))
		}
		return
	}
	if opt.Debug == true {
		DebugLogger.Debug(msg)
	} else {
		DebugLogger.Debug(msg, zap.Any("base", l.Base))
	}
	return
}

// Info info日志实现
func (l *LoggerImp) Info(msg string, fields ...interface{}) {
	if len(fields) > 0 {
		result := fields[0]
		if opt.Debug == true {
			InfoLogger.Info(msg, zap.Any("content", result))
		} else {
			InfoLogger.Info(msg, zap.Any("base", l.Base), zap.Any("content", result))
		}
		return
	}
	if opt.Debug == true {
		InfoLogger.Info(msg)
	} else {
		InfoLogger.Info(msg, zap.Any("base", l.Base))
	}
	return
}

// Warn warn日志实现
func (l *LoggerImp) Warn(msg string, fields ...interface{}) {
	if len(fields) > 0 {
		result := fields[0]
		if opt.Debug == true {
			WarnLogger.Warn(msg, zap.Any("content", result))
		} else {
			WarnLogger.Warn(msg, zap.Any("base", l.Base), zap.Any("content", result))
		}
		return
	}
	if opt.Debug == true {
		WarnLogger.Warn(msg)
	} else {
		WarnLogger.Warn(msg, zap.Any("base", l.Base))
	}
	return
}

// Error error日志实现
func (l *LoggerImp) Error(msg string, fields ...interface{}) {
	if len(fields) > 0 {
		result := fields[0]
		if opt.Debug == true {
			ErrorLogger.Error(msg, zap.Any("content", result))
		} else {
			ErrorLogger.Error(msg, zap.Any("base", l.Base), zap.Any("content", result))
		}
		return
	}
	if opt.Debug == true {
		ErrorLogger.Error(msg)
	} else {
		ErrorLogger.Error(msg, zap.Any("base", l.Base))
	}
	return
}
